/**
 * 
 */
package name.yzhu.httpserver;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.servlet.http.HttpServletResponse;

import name.yzhu.common.CommonConstant;
import name.yzhu.common.yaml.Yaml;

import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;

/**
 * http return data structure
 *<pre>  
 *     int  |     int        |  byte[] |   int     | byte[]
 *    ------|----------------|---------------------|-----
 *    status|   head length  |   head  |data length| data
 *</pre>
 *  
 *  if head is null, head length will be written 0 and head is not written any more, the heads is separated by \n , 
 *  and the key and value is identified by : </p>  for example:<pre> content-type: text/html\n content-code: utf-8</pre>
 *  the data is same as head </br>
 *  if data is null, data length will be written 0 and data is not written any more </br>
 *  </p>
 * @author yhzhu
 */
public class HttpReturnData {
	public static final int SC_SUCCESS = HttpServletResponse.SC_OK;
	private byte[] data = null;
	private int status=SC_SUCCESS;
	/**
	 * Use LinkedHashMap to keep the order of inputing head. 
	 */
	private Map<String, String> head = new LinkedHashMap<String, String>();
	
	public static final byte[] NORMAL_RESULT;
	static {
		HttpReturnData r = new HttpReturnData();
		r.setStatus(SC_SUCCESS);
		NORMAL_RESULT = r.toResult();
	}
	
	public HttpReturnData(){}
	
	public HttpReturnData(byte[] datas) {
		ByteArrayDataInput bsdi = ByteStreams.newDataInput(datas);
		status = bsdi.readInt();
		int headLen = bsdi.readInt();
		if (headLen > 0){
			byte[] heads = new byte[headLen];
			bsdi.readFully(heads);
			String headStr = new String(heads);
			String[] headLines = headStr.split("\n");
			if (headLines != null && headLines.length > 0) {
				for (String line : headLines) {
					String[] kv = line.split(":", 2);
					
					if (kv != null && kv.length == 2) {
						if(kv[0].equals("status")){
							this.status=Integer.parseInt(kv[1]);
						}else{
							head.put(kv[0], kv[1]);
						}
					}

				}
			}
		}
		
		int dataLen = bsdi.readInt();
		if (dataLen > 0){
			data = new byte[dataLen];
			bsdi.readFully(data);
		}
		
	}

	public byte[] toResult() {
		ByteArrayDataOutput bndo = ByteStreams.newDataOutput();
		bndo.writeInt(status);
		StringBuilder sb = new StringBuilder();
		for (Map.Entry<String, String> entry : this.head.entrySet()) {
			sb.append(entry.getKey());
			sb.append(":");
			sb.append(entry.getValue());
			sb.append("\n");
		}
		if (sb.length() > 0){
			byte[] metaData = sb.toString().getBytes(CommonConstant.CHARSET_UTF_8);
			bndo.writeInt(metaData.length);
			bndo.write(metaData);
		}
		else {
			bndo.writeInt(0);
		}
		if (this.data == null) {
			bndo.writeInt(0);
		}
		else {
			bndo.writeInt(this.data.length);
			bndo.write(this.data);
		}
		return bndo.toByteArray();
	}
	
	public void setStatus(int status){
		this.status=status;
	}

	public int getStatus() {
		return this.status;
	}
	
	public void addHead(String name,String value){
		this.head.put(name, value);
	}
	
	public void removeHead(String name){
		this.head.remove(name);
	}

	public String getHead(String name) {
		return head.get(name);
	}

	public byte[] getData() {
		return data;
	}
	
	public void setData(byte[] data){
		this.data=data;
	}
	public Set<Entry<String, String>> heads(){
		return head.entrySet();
	}
	public static byte[] toErrorResult(int statusCode, String msg) {
		HttpReturnData r = new HttpReturnData();
		r.setStatus(statusCode);
		if (msg != null) {
			r.setData(msg.getBytes(CommonConstant.CHARSET_UTF_8));
		}
		return r.toResult();
	}

	@Override
	public String toString() {
		return "HttpReturnData [status=" + status + "\nhead=" + Yaml.Dump(head)+"]";
	}
	
}
